#pragma once

#include "requestor.hpp"
#include<vector>
//服务注册和发现的客户端
namespace RPC_project
{
    namespace client
    {
        //服务注册方客户端
        class Provider
        {
        public:
            using ptr =std::shared_ptr<Provider>;
            Provider(const Requestor::ptr requestor)
                :_requestor(requestor)
            {}

            //服务注册  告知注册中心哪一个主机的哪一个服务进行注册
            bool registryMethod(const BaseConnection::ptr &conn ,const std::string& method ,const Address& host)
            {
                //组织请求
                auto base_req =MessageFactory::Create(MType::REQ_SERVICE);
                RPC_project::ServiceRequest::ptr msg_req= std::dynamic_pointer_cast<RPC_project::ServiceRequest>(base_req);
                    msg_req->Setid(UUID::uuid());
                    msg_req->SetMyType(MType::REQ_SERVICE);
                    msg_req->SetMethod(method);
                    msg_req->SetHost(host);
                    msg_req->SetOpType(ServiceOptype::SERVICE_REGISTRY);
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->sync_send(conn ,msg_req ,msg_rsp);//同步发送
                if(ret == false)
                {
                    ELOG("%s 服务注册失败" ,method.c_str());
                    return false;
                }
                RPC_project::ServiceResponse::ptr service_rsp = std::dynamic_pointer_cast<RPC_project::ServiceResponse>(msg_rsp);
                if(service_rsp.get() == nullptr)
                {
                    ELOG("相应类型向下（子类）转换失败");
                    return false;
                }
                if(service_rsp->Rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务注册失败 原因 : %s" , errReason(service_rsp->Rcode()).c_str() );
                    return false;
                }
                //服务注册完毕
                return true;
            }
        private:
            Requestor::ptr _requestor;

        };

        //服务注册方客户端
        //客户端会在一次服务发现中，会获取多个可提供服务的主机地址信息 ，请求谁更合理
        //用负载均衡的思想： RR轮转
        class MethodHost
        {
        public:
            using ptr =std::shared_ptr<MethodHost>;
            MethodHost(const std::vector<Address> & hosts)
                :_hosts(hosts)
                ,_idx(0)
            {}
            MethodHost():_idx(0)
            {}

            //中途收到服务上线请求后，添加主机
            void appendHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);

            }
            //中途收到服务下线请求后，删除主机
            void removeHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);

                for(auto it = _hosts.begin() ;it != _hosts.end() ;++it)
                {
                    if(*it == host)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }
            //选择一个主机 ,选择接口的调用大于上/下线的接口调用
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos =_idx++ % _hosts.size();
                return _hosts[pos];
            }

            bool empty()
            {
                std::unique_lock<std::mutex> lock(_mutex);

                return _hosts.empty();
            }
        private:
            std::mutex _mutex;
            size_t _idx;              //代表当前取到第几个了
            std::vector<Address> _hosts;
        };


        class Discovery
        {
        public:
            using OfflineCallback =std::function<void(const Address&)>;
            using ptr =std::shared_ptr<Discovery>;

            Discovery(const  Requestor::ptr &requestor , const OfflineCallback &cb)
                :_requestor(requestor)
                ,_offlineCallback(cb)
            {}
            bool serviceDiscovery(const BaseConnection::ptr &conn, std::string method, Address &host)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it =_method_hosts.find(method);
                    //有该提供服务的主机信息
                    if(it != _method_hosts.end())
                    {
                        if(it->second->empty() == false)
                        {
                            host =it->second->chooseHost();
                            return true;
                        }
                    }
                }

                //发现没有该提供服务的主机信息或者没有方法
                //进行服务发现request
                auto base_req =MessageFactory::Create(MType::REQ_SERVICE);
                RPC_project::ServiceRequest::ptr msg_req= std::dynamic_pointer_cast<RPC_project::ServiceRequest>(base_req);
                msg_req->Setid(UUID::uuid());
                msg_req->SetMethod(method);
                msg_req->SetMyType(MType::REQ_SERVICE);
                msg_req->SetOpType(ServiceOptype::SERVICE_DISCOVERY);
                BaseMessage::ptr msg_rsp;
                bool ret =_requestor->sync_send(conn , msg_req , msg_rsp);
                if(ret ==false)
                {
                    ELOG("服务发现失败");
                    return false;
                }
                auto service_rsp =std::dynamic_pointer_cast<RPC_project::ServiceResponse>(msg_rsp);
                if(!service_rsp )
                {
                    ELOG("服务发现失败 ，响应类型失败");
                    return false;
                }
                if(service_rsp->Rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务发现失败 ，原因：%s" ,errReason(service_rsp->Rcode()).c_str());
                    return false;
                }

                std::unique_lock<std::mutex> lock(_mutex);
                auto method_host =std::make_shared<MethodHost>(service_rsp->Hosts());
                if(method_host->empty())
                {
                    ELOG("发现服务失败 ，没有可以提供该服务 :%s 的主机" ,method.c_str());
                    return false;
                }
                _method_hosts[method] = method_host;
                host = method_host->chooseHost();
                return true;

            }


            //根据不同的服务，对(上线request或者下线request)进行处理的回调函数 。会提交给dispatcher
            //接受servicerequest 返回serviceresponse
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                //判断是上线还是下线 ,都不是的话就不处理
                //上线请求 ，找到methodHost ,向其中新增一个主机
                std::string method =msg->Method();
                auto optype = msg->OpType();
                std::unique_lock<std::mutex> lock(_mutex);
                if(optype ==ServiceOptype::SERVICE_ONLINE)
                {
                    
                    auto it = _method_hosts.find(method);
                    if(it == _method_hosts.end())
                    {
                        //没找到
                        auto method_hosts =std::make_shared<MethodHost>();
                        method_hosts->appendHost(msg->Host());
                        _method_hosts[method] = method_hosts;
                    }
                    //找到了
                    it->second->appendHost(msg->Host());
                }
                else if(optype ==ServiceOptype::SERVICE_OFFLINE)
                {
                    //下线请求 ，找到methodHost ，从其中删除一个主机
                    auto it = _method_hosts.find(method);
                    if(it == _method_hosts.end())
                    {
                        return ;
                    }
                    it->second->removeHost(msg->Host());
                    _offlineCallback(msg->Host());//主机下线进行删除
                }
                // 都不是的话就不处理


            }

        private:
            OfflineCallback _offlineCallback;
            std::mutex _mutex;
            std::unordered_map<std::string  ,MethodHost::ptr>  _method_hosts;
            Requestor::ptr _requestor;
        };
    }

}